///\todo upgrade

//////////////////////////////////////////////////////////////////////////////////
//                                                                              //
//  This file is part of the buola project (https://code.google.com/p/buola/).  //
//                                                                              //
//  Copyright(c) 2007-2012 Xavi Gratal                                          //
//  gratal AT gmail DOT com                                                     //
//                                                                              //
//  Buola is free software: you can redistribute it and/or modify               //
//  it under the terms of the GNU General Public License as published by        //
//  the Free Software Foundation, either version 3 of the License, or           //
//  (at your option) any later version.                                         //
//                                                                              //
//  Buola is distributed in the hope that it will be useful,                    //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of              //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               //
//  GNU General Public License for more details.                                //
//                                                                              //
//  You should have received a copy of the GNU General Public License           //
//  along with buola.  If not, see <http://www.gnu.org/licenses/>.              //
//                                                                              //
//////////////////////////////////////////////////////////////////////////////////

#ifndef _BUOLA_IMAGE_ALGORITHM_MISC_H_
#define _BUOLA_IMAGE_ALGORITHM_MISC_H_

#include <buola/image/algorithm/detail/opencv.h>

namespace buola { namespace img {

//! \name miscellaneous
//@{

template<typename tView>
inline typename tView::TPixel bilinear_interp(const tView &pView,double pX,double pY)
{
    typedef typename tView::TPixel TPixel;

    double lFX=std::floor(pX);
    double lFY=std::floor(pY);
    double lX1=pX-lFX;
    double lX0=1.0-lX1;
    double lY1=pY-lFY;
    double lY0=1.0-lY1;

    TPixel lPixel;

    auto lP00=pView((int)lFX,(int)lFY);
    auto lP10=pView((int)lFX+1,(int)lFY);
    auto lP01=pView((int)lFX,(int)lFY+1);
    auto lP11=pView((int)lFX+1,(int)lFY+1);

    for(int c=0;c<tView::cChannels;++c)
    {
        lPixel[c]=lP00[c]*lX0*lY0+lP10[c]*lX1*lY0+lP01[c]*lX0*lY1+lP11[c]*lX1*lY1;
    }

    return lPixel;
}

inline void radial_correct(const CView_rgb8 &pSrc,const CMView_rgb8 &pDst,
            double pAx,double pAy)
{
    for(int y=0;y<pSrc.Height();++y)
    {
        auto lDstIt=pDst.RowBegin(y);

        for(int x=0;x<pSrc.Width();++x)
        {
            double lNX=((double)x/(double)pSrc.Width()*2.0)-1.0;
            double lNY=((double)y/(double)pSrc.Height()*2.0)-1.0;
            double lMod2=lNX*lNX+lNY*lNY;

            double lRX=lNX*(1-pAx*lMod2);
            double lRY=lNY*(1-pAy*lMod2);

            lRX=(lRX+1.0)*pSrc.Width()/2.0;
            lRY=(lRY+1.0)*pSrc.Height()/2.0;

            if(lRX<0||lRX>=pSrc.Width()||lRY<0||lRY>pSrc.Height())
            {
                for(int c=0;c<CView_rgb8::cChannels;++c)
                {
                    lDstIt[x][c]=0;
                }
            }
            else
            {
                lDstIt[x]=bilinear_interp(pSrc,lRX,lRY);
            }
        }
    }
}

inline void contour(const CView_gray8 &pSrc,const CMView_gray8 &pDst)
{
    for (int y=1; y<pSrc.Height()-1; ++y)
    {
        for (int x=1; x<pSrc.Width()-1; ++x)
        {
            if(pSrc(x,y)==0)
                pDst(x,y)=0;
            else
            {
                unsigned char lAnd=pSrc(x-1,y)&pSrc(x+1,y)&pSrc(x,y+1)&pSrc(x,y-1);
                if(lAnd)
                    pDst(x,y)=0;
                else
                    pDst(x,y)=255;
            }
        }
    }
}

inline void encode_image(const CView_rgb8 &pSrc,const CMView_rgb8 &pDst)
{
    int lPrevious[3]={0,0,0};

    for(int y=0;y<pSrc.Height();++y)
    {
        auto lSrcIt=pSrc.RowBegin(y);
        auto lDstIt=pDst.RowBegin(y);

        for(int x=0;x<pSrc.Width();++x)
        {
            lDstIt[x][0]=lSrcIt[x][0]-lPrevious[0];
            lPrevious[0]=lSrcIt[x][0];

            lDstIt[x][1]=lSrcIt[x][1]-lPrevious[1];//-lDstIt[x][0];
            lPrevious[1]=lSrcIt[x][1];

            lDstIt[x][2]=lSrcIt[x][2]-lPrevious[2];//-lDstIt[x][0];
            lPrevious[2]=lSrcIt[x][2];
        }
    }
}

inline void decode_image(const CView_rgb8 &pSrc,const CMView_rgb8 &pDst)
{
    int lPrevious[3]={0,0,0};

    for(int y=0;y<pSrc.Height();++y)
    {
        auto lSrcIt=pSrc.RowBegin(y);
        auto lDstIt=pDst.RowBegin(y);

        for(int x=0;x<pSrc.Width();++x)
        {
            lDstIt[x][0]=lSrcIt[x][0]+lPrevious[0];
            lPrevious[0]=lDstIt[x][0];

            lDstIt[x][1]=lSrcIt[x][1]+lPrevious[1];//+lSrcIt[x][0];
            lPrevious[1]=lDstIt[x][1];

            lDstIt[x][2]=lSrcIt[x][2]+lPrevious[2];//+lSrcIt[x][0];
            lPrevious[2]=lDstIt[x][2];
        }
    }
}

//@}
    
/*namespace image*/ } /*namespace buola*/ }

#endif
